"""百度AI开放平台API适配器"""

import time
import base64
import json
from typing import Dict, Any, List, Optional, Union, IO

from cn_api_adapters.common.base import BaseAPI
from cn_api_adapters.common.utils import generate_nonce, generate_timestamp
from cn_api_adapters.common.baiduai.exceptions import (
    BaiduAIError, BaiduAIAuthError, BaiduAITokenError,
    BaiduAIRateLimitError, BaiduAINotEnoughQuotaError,
    BaiduAIParamError, BaiduAIImageError, BaiduAITextError,
    BaiduAISpeechError, BaiduAILanguageError, BaiduAIUnitError,
    BaiduAIASRError, BaiduAITTSInvalidError
)


class BaiduAI(BaseAPI):
    """
    百度AI开放平台API适配器
    
    提供百度AI开放平台各类API的调用功能，包括：
    - 文字识别（OCR）
    - 语音识别与合成
    - 人脸识别
    - 自然语言处理
    - 智能对话（UNIT）
    - 图像识别与分析
    """
    
    BASE_URL = "https://aip.baidubce.com"
    TOKEN_URL = "https://aip.baidubce.com/oauth/2.0/token"
    
    def __init__(self, 
                 api_key: Optional[str] = None,
                 secret_key: Optional[str] = None,
                 access_token: Optional[str] = None,
                 timeout: int = 30,
                 max_retries: int = 3,
                 retry_delay: float = 1.0,
                 debug: bool = False):
        """
        初始化百度AI API
        
        Args:
            api_key: 应用的API Key
            secret_key: 应用的Secret Key
            access_token: 直接提供access_token（可选）
            timeout: 请求超时时间（秒）
            max_retries: 最大重试次数
            retry_delay: 重试间隔（秒）
            debug: 是否开启调试模式
        """
        super().__init__(timeout=timeout, 
                         max_retries=max_retries, 
                         retry_delay=retry_delay,
                         debug=debug)
        
        self.api_key = api_key
        self.secret_key = secret_key
        self._access_token = access_token
        self._token_expires_at = 0
        self._token_lock = False
    
    @property
    def access_token(self) -> str:
        """
        获取AccessToken（自动刷新）
        
        Returns:
            AccessToken字符串
            
        Raises:
            BaiduAIAuthError: 认证失败
        """
        # 检查是否需要刷新token
        if not self._access_token or time.time() > self._token_expires_at - 60:
            self._refresh_access_token()
        return self._access_token
    
    def _refresh_access_token(self):
        """
        刷新AccessToken
        
        Raises:
            BaiduAIAuthError: 认证失败
        """
        # 防止并发刷新token
        if hasattr(self, '_token_lock') and self._token_lock:
            # 等待其他线程完成token刷新
            import threading
            for _ in range(10):
                if not self._token_lock:
                    return
                time.sleep(0.1)
        
        self._token_lock = True
        try:
            # 再次检查token是否需要刷新（可能被其他线程刷新）
            if self._access_token and time.time() <= self._token_expires_at - 60:
                return
            
            if not self.api_key or not self.secret_key:
                raise BaiduAIAuthError("缺少API Key或Secret Key")
            
            params = {
                "grant_type": "client_credentials",
                "client_id": self.api_key,
                "client_secret": self.secret_key
            }
            
            response = self.get(self.TOKEN_URL, params=params)
            
            if "error" in response:
                error_code = response.get("error", "unknown")
                error_description = response.get("error_description", "获取AccessToken失败")
                raise BaiduAITokenError(error_description, error_code, response)
            
            if "access_token" not in response:
                raise BaiduAITokenError("获取AccessToken失败：响应中缺少access_token", "missing_token", response)
            
            self._access_token = response["access_token"]
            expires_in = response.get("expires_in", 2592000)  # 默认30天
            self._token_expires_at = time.time() + expires_in
        finally:
            self._token_lock = False
    
    def _handle_response(self, response: Dict[str, Any]) -> Dict[str, Any]:
        """
        处理API响应
        
        Args:
            response: API响应数据
            
        Returns:
            处理后的响应数据
            
        Raises:
            BaiduAIError: API调用失败
        """
        # 百度AI API错误格式：{"error_code": 错误码, "error_msg": "错误消息"}
        if isinstance(response, dict):
            if "error_code" in response and response["error_code"] != 0:
                error_code = str(response["error_code"])
                error_msg = response.get("error_msg", "未知错误")
                
                # 处理特定错误码
                
                # 认证相关错误
                if error_code in ["110", "111", "17", "18"]:
                    # 清除token，下次请求会自动刷新
                    self._access_token = None
                    raise BaiduAITokenError(error_msg, error_code, response)
                
                # 限流错误
                elif error_code in ["17", "18", "101", "102"]:
                    retry_after = response.get("retry_after", 5)
                    raise BaiduAIRateLimitError(error_msg, error_code, response, retry_after)
                
                # 配额不足
                elif error_code == "18":
                    raise BaiduAINotEnoughQuotaError(error_msg, error_code, response)
                
                # 参数错误
                elif error_code.startswith("1"):
                    raise BaiduAIParamError(error_msg, error_code, response)
                
                # 图片处理错误
                elif error_code in ["216100", "216101", "216110"]:
                    raise BaiduAIImageError(error_msg, error_code, response)
                
                # 文本处理错误
                elif error_code in ["100", "101", "102"]:
                    raise BaiduAITextError(error_msg, error_code, response)
                
                # 语音处理错误
                elif error_code in ["3300", "3301", "3302"]:
                    raise BaiduAISpeechError(error_msg, error_code, response)
                
                # 其他错误
                else:
                    raise BaiduAIError(error_msg, error_code, response)
        
        return response
    
    def _prepare_image_data(self, image: Union[str, bytes, IO]) -> str:
        """
        准备图片数据（转为base64编码）
        
        Args:
            image: 图片路径、图片字节数据或文件对象
            
        Returns:
            base64编码的图片字符串
        """
        if isinstance(image, str):
            # 文件路径
            try:
                with open(image, 'rb') as f:
                    image_data = f.read()
            except Exception as e:
                raise BaiduAIImageError(f"读取图片文件失败: {str(e)}")
        elif isinstance(image, bytes):
            # 字节数据
            image_data = image
        elif hasattr(image, 'read'):
            # 文件对象
            image_data = image.read()
        else:
            raise BaiduAIParamError("不支持的图片格式，请提供文件路径、字节数据或文件对象")
        
        # 转换为base64编码
        return base64.b64encode(image_data).decode('utf-8')
    
    # ========== 通用请求方法 ==========
    
    def ai_request(self, api_name: str, 
                  data: Dict[str, Any] = None,
                  method: str = "POST",
                  **kwargs) -> Dict[str, Any]:
        """
        发起百度AI API请求
        
        Args:
            api_name: API名称
            data: 请求数据
            method: 请求方法
            **kwargs: 其他参数
            
        Returns:
            API响应
        """
        url = f"{self.BASE_URL}{api_name}"
        params = {"access_token": self.access_token}
        
        if method.upper() == "GET":
            if data:
                params.update(data)
            return self.get(url, params=params, **kwargs)
        else:
            return self.post(url, params=params, data=data, **kwargs)
    
    # ========== 文字识别（OCR）API ==========
    
    def ocr_general(self, image: Union[str, bytes, IO], 
                   language_type: str = "CHN_ENG",
                   detect_direction: bool = False,
                   detect_language: bool = False,
                   probability: bool = False) -> Dict[str, Any]:
        """
        通用文字识别
        
        Args:
            image: 图片（路径、字节数据或文件对象）
            language_type: 识别语言类型
            detect_direction: 是否检测图像朝向
            detect_language: 是否检测语言
            probability: 是否返回识别结果中每一行的置信度
            
        Returns:
            识别结果
        """
        image_base64 = self._prepare_image_data(image)
        data = {
            "image": image_base64,
            "language_type": language_type,
            "detect_direction": detect_direction,
            "detect_language": detect_language,
            "probability": probability
        }
        return self.ai_request("/rest/2.0/ocr/v1/general", data=data)
    
    def ocr_basic(self, image: Union[str, bytes, IO], 
                  detect_direction: bool = False,
                  probability: bool = False) -> Dict[str, Any]:
        """
        通用文字识别（高精度版）
        
        Args:
            image: 图片（路径、字节数据或文件对象）
            detect_direction: 是否检测图像朝向
            probability: 是否返回识别结果中每一行的置信度
            
        Returns:
            识别结果
        """
        image_base64 = self._prepare_image_data(image)
        data = {
            "image": image_base64,
            "detect_direction": detect_direction,
            "probability": probability
        }
        return self.ai_request("/rest/2.0/ocr/v1/accurate", data=data)
    
    def ocr_id_card(self, image: Union[str, bytes, IO], 
                   id_card_side: str = "front",
                   detect_direction: bool = False,
                   detect_risk: bool = False) -> Dict[str, Any]:
        """
        身份证识别
        
        Args:
            image: 图片（路径、字节数据或文件对象）
            id_card_side: 身份证正反面（front/back）
            detect_direction: 是否检测图像朝向
            detect_risk: 是否开启风险类型识别
            
        Returns:
            识别结果
        """
        image_base64 = self._prepare_image_data(image)
        data = {
            "image": image_base64,
            "id_card_side": id_card_side,
            "detect_direction": detect_direction,
            "detect_risk": detect_risk
        }
        return self.ai_request("/rest/2.0/ocr/v1/idcard", data=data)
    
    def ocr_business_license(self, image: Union[str, bytes, IO],
                           detect_direction: bool = False) -> Dict[str, Any]:
        """
        营业执照识别
        
        Args:
            image: 图片（路径、字节数据或文件对象）
            detect_direction: 是否检测图像朝向
            
        Returns:
            识别结果
        """
        image_base64 = self._prepare_image_data(image)
        data = {
            "image": image_base64,
            "detect_direction": detect_direction
        }
        return self.ai_request("/rest/2.0/ocr/v1/business_license", data=data)
    
    # ========== 语音识别（ASR）API ==========
    
    def speech_recognition(self, audio: Union[str, bytes, IO],
                          dev_pid: int = 1537,
                          rate: int = 16000,
                          format: str = "wav") -> Dict[str, Any]:
        """
        语音识别
        
        Args:
            audio: 音频文件（路径、字节数据或文件对象）
            dev_pid: 语言模型
            rate: 采样率
            format: 音频格式
            
        Returns:
            识别结果
        """
        if isinstance(audio, str):
            try:
                with open(audio, 'rb') as f:
                    audio_data = f.read()
            except Exception as e:
                raise BaiduAISpeechError(f"读取音频文件失败: {str(e)}")
        elif isinstance(audio, bytes):
            audio_data = audio
        elif hasattr(audio, 'read'):
            audio_data = audio.read()
        else:
            raise BaiduAIParamError("不支持的音频格式")
        
        audio_base64 = base64.b64encode(audio_data).decode('utf-8')
        data = {
            "format": format,
            "rate": rate,
            "channel": 1,
            "cuid": "python-sdk",
            "token": self.access_token,
            "speech": audio_base64,
            "len": len(audio_data),
            "dev_pid": dev_pid
        }
        
        url = "https://vop.baidu.com/server_api"
        return self.post(url, json=data)
    
    # ========== 语音合成（TTS）API ==========
    
    def text_to_speech(self, text: str,
                      lang: str = "zh",
                      ctp: int = 1,
                      per: int = 0,
                      spd: int = 5,
                      pit: int = 5,
                      vol: int = 5,
                      aue: int = 6,
                      save_path: Optional[str] = None) -> Union[bytes, str]:
        """
        语音合成
        
        Args:
            text: 待合成的文本
            lang: 语言（zh-中文，en-英文）
            ctp: 客户端类型（1-Web）
            per: 发音人选择（0-度小宇，1-度小美，3-逍遥，4-度逍遥，5-度丫丫，103-度米朵）
            spd: 语速（0-9）
            pit: 音调（0-9）
            vol: 音量（0-15）
            aue: 音频格式（3-mp3，6-wav）
            save_path: 保存路径（可选）
            
        Returns:
            音频数据（bytes）或保存成功的路径
        """
        params = {
            "tok": self.access_token,
            "tex": text,
            "cuid": "python-sdk",
            "lan": lang,
            "ctp": ctp,
            "per": per,
            "spd": spd,
            "pit": pit,
            "vol": vol,
            "aue": aue
        }
        
        url = "https://tsn.baidu.com/text2audio"
        response = self._session.get(url, params=params, stream=True)
        response.raise_for_status()
        
        audio_data = response.content
        
        # 检查是否为错误响应（JSON格式）
        try:
            error_info = json.loads(audio_data)
            if "err_no" in error_info and error_info["err_no"] != 0:
                error_no = str(error_info["err_no"])
                error_msg = error_info.get("err_msg", "语音合成失败")
                raise BaiduAITTSInvalidError(error_msg, error_no, error_info)
        except json.JSONDecodeError:
            # 不是JSON，是有效的音频数据
            pass
        
        # 保存文件
        if save_path:
            with open(save_path, 'wb') as f:
                f.write(audio_data)
            return save_path
        
        return audio_data
    
    # ========== 自然语言处理API ==========
    
    def nlp_lexer(self, text: str, 
                 mode: int = 1,
                 pos_tag: bool = True) -> Dict[str, Any]:
        """
        词法分析
        
        Args:
            text: 待分析文本
            mode: 模式（0-默认模式，1-高精度模式）
            pos_tag: 是否需要词性标注
            
        Returns:
            分析结果
        """
        data = {
            "text": text,
            "mode": mode,
            "pos_tag": pos_tag
        }
        return self.ai_request("/rpc/2.0/nlp/v1/lexer", data=data)
    
    def nlp_sentiment_classify(self, text: str) -> Dict[str, Any]:
        """
        情感倾向分析
        
        Args:
            text: 待分析文本
            
        Returns:
            分析结果
        """
        data = {"text": text}
        return self.ai_request("/rpc/2.0/nlp/v1/sentiment_classify", data=data)
    
    def nlp_keyword(self, title: str, 
                   content: str,
                   max_items: int = 5) -> Dict[str, Any]:
        """
        关键词提取
        
        Args:
            title: 标题
            content: 正文内容
            max_items: 最大关键词数量
            
        Returns:
            提取结果
        """
        data = {
            "title": title,
            "content": content,
            "max_items": max_items
        }
        return self.ai_request("/rpc/2.0/nlp/v1/keyword", data=data)
    
    # ========== 图像识别API ==========
    
    def image_classify(self, image: Union[str, bytes, IO],
                      top_num: int = 5) -> Dict[str, Any]:
        """
        通用物体识别
        
        Args:
            image: 图片（路径、字节数据或文件对象）
            top_num: 返回结果数量
            
        Returns:
            识别结果
        """
        image_base64 = self._prepare_image_data(image)
        data = {
            "image": image_base64,
            "top_num": top_num
        }
        return self.ai_request("/rest/2.0/image-classify/v1/animal", data=data)
    
    def image_face_detect(self, image: Union[str, bytes, IO],
                         face_field: str = "age,beauty,expression,faceshape,gender,glasses",
                         max_face_num: int = 1) -> Dict[str, Any]:
        """
        人脸识别
        
        Args:
            image: 图片（路径、字节数据或文件对象）
            face_field: 人脸特征（可选：age,beauty,expression,faceshape,gender,glasses）
            max_face_num: 最多处理人脸数量
            
        Returns:
            识别结果
        """
        image_base64 = self._prepare_image_data(image)
        data = {
            "image": image_base64,
            "face_field": face_field,
            "max_face_num": max_face_num
        }
        return self.ai_request("/rest/2.0/face/v3/detect", data=data)


class BaiduOCR(BaiduAI):
    """
    百度OCR专用类
    简化OCR相关API的调用方式
    """
    
    def __init__(self, 
                 api_key: Optional[str] = None,
                 secret_key: Optional[str] = None,
                 access_token: Optional[str] = None):
        """
        初始化百度OCR
        
        Args:
            api_key: 应用的API Key
            secret_key: 应用的Secret Key
            access_token: 直接提供access_token（可选）
        """
        super().__init__(api_key=api_key, 
                         secret_key=secret_key,
                         access_token=access_token)


class BaiduSpeech(BaiduAI):
    """
    百度语音专用类
    简化语音识别与合成API的调用方式
    """
    
    def __init__(self, 
                 api_key: Optional[str] = None,
                 secret_key: Optional[str] = None,
                 access_token: Optional[str] = None):
        """
        初始化百度语音
        
        Args:
            api_key: 应用的API Key
            secret_key: 应用的Secret Key
            access_token: 直接提供access_token（可选）
        """
        super().__init__(api_key=api_key, 
                         secret_key=secret_key,
                         access_token=access_token)


class BaiduNLP(BaiduAI):
    """
    百度自然语言处理专用类
    简化NLP相关API的调用方式
    """
    
    def __init__(self, 
                 api_key: Optional[str] = None,
                 secret_key: Optional[str] = None,
                 access_token: Optional[str] = None):
        """
        初始化百度NLP
        
        Args:
            api_key: 应用的API Key
            secret_key: 应用的Secret Key
            access_token: 直接提供access_token（可选）
        """
        super().__init__(api_key=api_key, 
                         secret_key=secret_key,
                         access_token=access_token)